#ifdef MINIMUM_MEMORY
  #define QUERYBUFFER_SIZE            1   // max queries to execute at a time
  #define QUERYJOINTARGETBUFFER_SIZE 10   // max number of join points
  #define QUERYSOURCEBUFFER_SIZE     10   // max number of targets
  #define MULTICASTCACHE_SIZE         3   // max number of cached multicast per split node
#else
  #define QUERYBUFFER_SIZE            3
  #define QUERYJOINTARGETBUFFER_SIZE 40
  #define QUERYSOURCEBUFFER_SIZE     20
  #define MULTICASTCACHE_SIZE        10
#endif

#define JOININDEX_BASE                0

#define QUERYOPT_SENDTOROOT      0x01  // query options constants
#define QUERYOPT_PRUNE           0x02
#define QUERYOPT_MULTICAST       0x04
#define QUERYOPT_CACHEMULTICAST  0x08
#define QUERYOPT_POPULATEWINDOW  0x10
#define QUERYOPT_ESTSEL          0x20

#define JOINEST_SEQWINDOW          10

#define MAX_QUERYCOUNT     10
#define MAX_EVALS           8
#define MAX_EXPR           20
#define MAX_TOKENS       1000
#define MAX_SUBTREES      100
#define MAX_ROUTEPRED      20
#define MAX_OPARG          10

#define REL_COUNT     3
#define SRCREL_COUNT  2 
#define REL_S      0
#define REL_T      1
#define REL_J      2

#if defined (TOSSIM) || defined (UNIT_TEST)
char *rel_name[REL_COUNT] = {"S", "T", "J"};
#endif

#define RELTYPE_COUNT  2  // static & dynamic
#define REL_STATIC   0
#define REL_DYNAMIC  1

#define PARAM_WINDOWADVANCETYPE 0
#define PARAM_WINDOWSIZE        1
#define PARAM_WINDOWADVANCETIME 2
#define PARAM_SAMPLEINTERVAL    3
#define PARAM_REROUTEINTERVAL   4
#define PARAM_BEGIN             5
#define PARAM_END               6
#define PARAM_GROUPOPT          7
#define PARAM_COUNT             8

#define WINDOWADVANCE_TUPLE     0
#define WINDOWADVANCE_TIME      1

#define TUPLEGET_NODE(t)                     ((t)[0])
#define TUPLEGET_DISABLEJOIN(t)              ((t)[1] >> 15)
#define TUPLEGET_SEQ(t)                      (((t)[1] >> 8) & 0x7f)
#define TUPLEGET_QUERY(t)                    (((t)[1] >> 4) & 0x0f)
#define TUPLEGET_REL(t)                      ((t)[1] & 0x07)
#define TUPLEGET_AGGR(t)                     (((t)[1] >> 3) & 0x01)
#define TUPLESET(t, src, d, seq, q, r, agg)  ((t)[0] = (src), (t)[1] = (((d) << 15) | ((seq) << 8) | ((q) << 4) | (r) | ((agg) << 3)))
#define TUPLESET_DISABLEJOIN(t, d)           ((t)[1] &= (0x7fff | ((d) << 15)))
#define TUPLESET_AGGR(t, agg)                ((t)[1] &= (0xfff7 | ((agg) << 3)))

#define TUPLEID_LEN          2
#define MAX_SUBRESULTS       (MAX_TUPLELEN-TUPLEID_LEN)

#define QUERYDISTRIB_FACTOR  5
#define QUERYINIT_FACTOR    30

typedef struct {                          // long running join
  uint8_t cmd;

  uint8_t query_id;
  uint8_t path_len, path_levels[MAX_PATHLEN];  // accumulated stats for optimum join point selection

  uint8_t eval_count;                          // static evaluations
  ATTRIBUTE eval[MAX_TUPLELEN];
} PACKING_MACRO INIT_MSGHI;

typedef struct {                          // join node nomination message (from the target)
  uint8_t cmd;

  uint8_t query_id;
  uint8_t seq_number;     // sequence number of join point update, enforce in-order updates at the source

  ADDRESS source_node;                    // address of the source node
  uint8_t path_nodecount;                 // path from the join node to the source node
  ADDRESS path_nodes[MAX_PATHLEN];

  uint8_t sel_update, sel_est[REL_COUNT];                // if sel_update = 1 - est_srcsel is set
  uint8_t remove_pair;       // if set will remove pair from old join node
} PACKING_MACRO CONFIRMJOIN_MSGHI;

typedef struct {                          // join node to source message
  uint8_t cmd;

  uint8_t query_id;
  uint8_t seq_number;                     // for in-order updates
  ADDRESS target_node;                    // which is the target of this pair
  uint8_t level;                          // distance of join point to base (for cost model)

  uint8_t sel_update, sel_est[REL_COUNT];                // if sel_update = 1 - est_srcsel is set
} PACKING_MACRO CONFIRMSOURCE_MSGHI;

typedef struct {                          // target node to source message
  uint8_t cmd;

  uint8_t query_id;
  uint8_t seq_number;                     // for in-order updates

  uint8_t sel_update, sel_est[REL_COUNT];                // if sel_update = 1 - est_srcsel is set
} PACKING_MACRO CONFIRMTARGETSOURCE_MSGHI;

typedef struct {                          // structure to maintain info during RepairRoute
  uint8_t cmd;

  uint8_t path_found, path_index;         // path_found - if we're still looking for a route, or we're returning to source
  ADDRESS neighbor_node, dest_node;       // path_index - at which position on repair_path did we find the destination
} PACKING_MACRO REPAIR_MSGHI;     // neighbor_node, dest_node - where the failure triggering the repair occured

typedef struct {                          // producer and join selectivities updates, received from join node at target
  uint8_t cmd;

  uint8_t query_id;
  ADDRESS source_node;
  uint8_t sel_est[REL_COUNT];
} PACKING_MACRO ESTUPDATE_MSGHI;

typedef struct {               // structure maintaining join points
  uint8_t used;

  ADDRESS id;
  uint8_t path_nodecount;      // path to join node
  ADDRESS *path_nodes;
  uint8_t level;               // distance to base

  uint32_t tuples_sent[SRCREL_COUNT];        // tuples sent to this join node, per relation
} PACKING_MACRO QUERYJOINTARGET_ENTRY;

typedef struct {               // structure mainitaing other nodes with which we join, and which join node we use to join
  uint8_t used;

  uint8_t query_id;            // which query is this pair for
  uint8_t rel_id;              // which relation are WE for this pair
  ADDRESS id;                  // other node of the pair
  uint8_t seq_number;          // used to accept in-order updates
  uint32_t pair_cost;          // best estimated cost of the join so far (computed by the target)
  int8_t jointarget_index;     // which join node are we using to join with this other node

  uint8_t path_len;            // path to other source (if any)
  ADDRESS *path;
  uint8_t *path_levels;

  uint8_t sel[REL_COUNT];        // current selectivity of the pair
} PACKING_MACRO QUERYSOURCE_ENTRY;

#define MAX_QUERYTOKENS 100
typedef struct {
  uint8_t token_count;
  TOKEN token[MAX_QUERYTOKENS];
  uint8_t attr_used[DIV_CEIL(MAX_ATTRDESC, 8)];
} PACKING_MACRO TOKEN_LIST;

typedef struct {
  STACK pred;

  uint8_t eval_count, eval_aggrcount;
  struct {
    STACK eval;
    uint8_t is_aggr;
  } PACKING_MACRO eval[MAX_EVALS];
  int8_t id_index;
} PACKING_MACRO RELSPEC_ENTRY;

typedef struct {
  STACK pred;
  uint8_t summary_index, lastchild_ofs;
} PACKING_MACRO ROUTINGPREDICATE_ENTRY;

typedef struct {
  uint8_t srcrel_count;
  RELSPEC_ENTRY rel[RELTYPE_COUNT][REL_COUNT];
  TOKEN_LIST token_list;

  uint8_t routingpredicate_count;
  ROUTINGPREDICATE_ENTRY routing_predicates[MAX_ROUTEPRED];

  uint32_t params[PARAM_COUNT];
  uint8_t sel_est[REL_COUNT];
} PACKING_MACRO QUERYSPEC_ENTRY;

typedef struct {
  uint8_t cmd;

  uint8_t query_id, running;
  QUERYSPEC_ENTRY spec;
} PACKING_MACRO QUERYSPEC_MSGHI;

typedef struct {
  uint8_t cmd;

  uint8_t query_id, running;
  QUERYSPEC_ENTRY *spec;
} PACKING_MACRO QUERYSPECP_MSGHI;

typedef struct {
  uint8_t buffer_len, *buffer;

  ADDRESS node, *path;
  uint8_t path_len;
  ADDRESS prev_node, *prev_path;
  uint8_t prev_pathlen;

  uint8_t improved;    // 1 if a better multicast is found through path collapse
  uint32_t cost, lastsent_cost, innet_cost, base_cost;  // this node's cost estimate for running the query in net and base
  uint8_t innet_seq;                // sequence number of in net plan cost
} PACKING_MACRO MULTICAST_ENTRY;

typedef struct {
  uint32_t tuples_sent, nextsampling_cycle;
  ATTRIBUTE *history_buffer;   // cached last few readings, replay to Base in case of failure of join node
  MULTICAST_ENTRY multicast;

  uint16_t est_tuplecount;
  uint32_t est_starttime;   // used for selectivity estimation
} PACKING_MACRO REL_ENTRY;

typedef struct {               // structure for maintaing query information
  uint8_t used;

  uint8_t query_id;
  QUERYSPEC_ENTRY spec;
  REL_ENTRY rel[SRCREL_COUNT];
  GROUPOPT_ENTRY groupopt;

  uint8_t innet_seq;       // seqence of join node updates, used for group optimization
  uint8_t sel_est[REL_COUNT];  // current selectivity estimates
  uint8_t tuple_len[REL_COUNT], aggrtuple_len[REL_COUNT];   // number of fields in tuples for each relation, non aggr & aggr cases

  uint8_t options;
  uint8_t initiated;
} PACKING_MACRO QUERY_ENTRY;

QUERYJOINTARGET_ENTRY queryjointarget_buffer[QUERYJOINTARGETBUFFER_SIZE];
QUERYSOURCE_ENTRY querysource_buffer[QUERYSOURCEBUFFER_SIZE];
QUERY_ENTRY query_buffer[QUERYBUFFER_SIZE];

void init_mqueryplan();
void query_sourceinitiate(uint8_t query_id);
int find_sourcenode(ADDRESS node_id);
uint8_t is_singlerel(uint8_t query_id, uint8_t rel_id);
uint8_t source_count(uint8_t query_id);

void handle_initmsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handle_estupdatemsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handle_confirmjoinmsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handlefail_confirmjoinmsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handle_confirmsourcemsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handle_confirmtargetsourcemsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handlefail_confirmtargetsourcemsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handle_repairmsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handle_querymsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handle_treesdonemsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);

void handlerouteinter_init(ROUTE_MSG *msg);
void handlerouteinter_repair(ROUTE_MSG *msg);
uint32_t evalpath_init(ROUTE_MSG *msg);

int find_queryindex(uint8_t query_id);
void optimize_plan();
uint8_t jointarget_multiplicity(QUERY_ENTRY *q, uint8_t rel_id, int jointarget_index);
uint8_t jointarget_anymultiplicity(int jointarget_index);

uint8_t queryplan_resolvepath(ADDRESS dest_node, ADDRESS *next_hop, ADDRESS *path, uint8_t *path_len);
uint8_t query_sourcecheckactivate(uint8_t query_id);
uint8_t query_targetcheckactivate(uint8_t query_id, ADDRESS source_node, uint8_t tuplelen_s, ATTRIBUTE *tuple_s);

int query_getjoinnode(ADDRESS join_node, uint8_t path_nodecount, ADDRESS *path_nodes, uint8_t level);

uint8_t doupdate_joinnode(QUERY_ENTRY *q, int querysource_index);


